﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ComputerArchitecture.Core;
using ComputerArchitecture.Core.Logging;
using ComputerArchitecture.Numbers;
using ComputerArchitecture.Units.Integers.Unsigned.Interfaces;

namespace ComputerArchitecture.Units.Integers.Unsigned.Implementations
{
    public class UnsignedMultiplyUnit : LoggingObject, IUnsignedMultiplyUnit
    {
        #region Fields

        private readonly IUnsignedAdderUnit _unsignedAdderUnit;

        #endregion

        #region Constructors

        public UnsignedMultiplyUnit(ILoggingService loggingService, IUnsignedAdderUnit unsignedAdderUnit)
            : base(loggingService)
        {
            ArgumentValidator.NullValidate(unsignedAdderUnit, "unsignedAdderUnit");
            _unsignedAdderUnit = unsignedAdderUnit;
        }

        public UnsignedMultiplyUnit(ILoggingService loggingService)
            : this(loggingService, new UnsignedAdderUnit(loggingService))
        {
        }

        #endregion

        IBitNumber IMultiplyUnit.Multiply(IBitNumber first, IBitNumber second)
        {
            return this.Multiply((IUnsignedBitNumber) first, (IUnsignedBitNumber) second);
        }

        public IUnsignedBitNumber Multiply(IUnsignedBitNumber first, IUnsignedBitNumber second)
        {
            if (ReferenceEquals(first, null))
            {
                throw new ArgumentNullException("first");
            }
            if (ReferenceEquals(second, null))
            {
                throw new ArgumentNullException("second");
            }
            if (first.Size != second.Size)
            {
                throw new IncompatibleSizeException();
            }
            if (first.IsZero || second.IsZero)
            {
                return new UnsignedBitNumber(first.Size << 1);
            }

            IUnsignedBitNumber result = new UnsignedBitNumber(first.Size << 1), longFirst = new UnsignedBitNumber(first.Size << 1);
            first.CopyTo(longFirst, first.Size, 0, first.Size);
            
            for (int i = 0; i < first.Size; ++i)
            {
                Bit carryBit;
                result.LogicShiftLeft(1, out carryBit);
                if (second[i].IsSet)
                {
                    bool overflow;
                    result = _unsignedAdderUnit.Add(result, longFirst, out overflow);
                }
            }
            return result;
        }
    }
}
